import { Matrix4 } from "../../utils/Matrix4";
import { MatrixUtil } from "../../utils/MatrixUtil";
import { TextrueUtil } from "../../utils/TextureUtil";
import { Util } from "../../utils/Util";
import { WebGLUtil } from "../../utils/WebGLUtil";
import { Render } from "../Render";
import { WebglTemplateBase } from "./WebglTemplateBase";

/*
 * @Author: Snow
 * @Date: 2021-10-11 15:08:49
 * @Description: 
 */
export class TemplateTest extends WebglTemplateBase {

    protected override initUITools(): void {
        super.initUITools();
    }

    protected override initProgram(): void {
        this.vsSource = `
            uniform mat4 u_worldViewProjection;
            uniform vec3 u_lightWorldPos;
            uniform mat4 u_world;
            uniform mat4 u_viewInverse;
            uniform mat4 u_worldInverseTranspose;

            attribute vec4 a_position;
            attribute vec3 a_normal;
            attribute vec2 a_texcoord;

            varying vec4 v_position;
            varying vec2 v_texCoord;
            varying vec3 v_normal;
            varying vec3 v_surfaceToLight;
            varying vec3 v_surfaceToView;

            void main() {
            v_texCoord = a_texcoord;
            v_position = (u_worldViewProjection * a_position);
            v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
            v_surfaceToLight = u_lightWorldPos - (u_world * a_position).xyz;
            v_surfaceToView = (u_viewInverse[3] - (u_world * a_position)).xyz;
            gl_Position = v_position;
            }
        `;

        this.fsSource = `
            precision mediump float;

            varying vec4 v_position;
            varying vec2 v_texCoord;
            varying vec3 v_normal;
            varying vec3 v_surfaceToLight;
            varying vec3 v_surfaceToView;
            
            uniform vec4 u_lightColor;
            uniform vec4 u_colorMult;
            uniform sampler2D u_diffuse;
            uniform vec4 u_specular;
            uniform float u_shininess;
            uniform float u_specularFactor;
            
            vec4 lit(float l ,float h, float m) {
            return vec4(1.0,
                        abs(l),
                        (l > 0.0) ? pow(max(0.0, h), m) : 0.0,
                        1.0);
            }
            
            void main() {
                vec4 diffuseColor = texture2D(u_diffuse, v_texCoord);
                vec3 a_normal = normalize(v_normal);
                vec3 surfaceToLight = normalize(v_surfaceToLight);
                vec3 surfaceToView = normalize(v_surfaceToView);
                vec3 halfVector = normalize(surfaceToLight + surfaceToView);
                vec4 litR = lit(dot(a_normal, surfaceToLight),
                                    dot(a_normal, halfVector), u_shininess);
                vec4 outColor = vec4((
                u_lightColor * (diffuseColor * litR.y * u_colorMult +
                                u_specular * litR.z * u_specularFactor)).rgb,
                    diffuseColor.a);
                gl_FragColor = outColor;
                //  gl_FragColor = vec4(litR.yyy, 1);
            }
        `;
        super.initProgram();
    }

    protected override initBuffers(): void {
        super.initBuffers();
        const gl = Render.GL;
        // an indexed quad
        let arrays = {
            position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
            texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], },
            normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], },
            indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], },
        };

        let bufferInfo = WebGLUtil.createBufferInfoFromArrays(gl, arrays);

        let cameraAngleRadians = Util.degToRad(0);
        let fieldOfViewRadians = Util.degToRad(60);
        let cameraHeight = 50;

        let uniformsThatAreTheSameForAllObjects = {
            u_lightWorldPos: [-50, 30, 100],
            u_viewInverse: Matrix4.identity([]),
            u_lightColor: [1, 1, 1, 1],
        };

        let uniformsThatAreComputedForEachObject = {
            u_worldViewProjection: Matrix4.identity([]),
            u_world: Matrix4.identity([]),
            u_worldInverseTranspose: Matrix4.identity([]),
        };

        let textures = [
            TextrueUtil.makeStripeTexture(gl, { color1: "#FFF", color2: "#CCC", }),
            TextrueUtil.makeCheckerTexture(gl, { color1: "#FFF", color2: "#CCC", }),
            TextrueUtil.makeCircleTexture(gl, { color1: "#FFF", color2: "#CCC", }),
        ];

        let objects = [];
        let numObjects = 300;
        let baseColor = Util.rand(240);
        for (var ii = 0; ii < numObjects; ++ii) {
            objects.push({
                radius: Util.rand(150),
                xRotation: Util.rand(Math.PI * 2),
                yRotation: Util.rand(Math.PI),
                materialUniforms: {
                    u_colorMult: [Util.rand(1, 2) / 2, 0.5, 1, 1],
                    u_diffuse: textures[Util.randInt(textures.length)],
                    u_specular: [1, 1, 1, 1],
                    u_shininess: Util.rand(500),
                    u_specularFactor: Util.rand(1),
                },
            });
        }

        let drawScene = () => {
            let time = this.ts;
            time =  time * 0.1 + 5;
            WebGLUtil.resizeCanvasToMatchDisplaySize(gl);
            // Clear the canvas AND the depth buffer.
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            // Compute the projection matrix
            let aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
            var projectionMatrix = Matrix4.perspective(fieldOfViewRadians, aspect, 1, 2000);

            // Compute the camera's matrix using look at.
            let cameraPosition = [0, 0, 100];
            let target = [0, 0, 0];
            let up = [0, 1, 0];
            let cameraMatrix = Matrix4.lookAt(cameraPosition, target, up);
            uniformsThatAreTheSameForAllObjects.u_viewInverse = cameraMatrix;

            // Make a view matrix from the camera matrix.
            let viewMatrix = Matrix4.invert(cameraMatrix);

            let viewProjectionMatrix = Matrix4.multiply(projectionMatrix, viewMatrix);

            gl.useProgram(this.programInfo.program);

            // Setup all the needed buffers and attributes.
            WebGLUtil.setBuffersAndAttributes(gl, this.programInfo, bufferInfo);

            // Set the uniforms that are the same for all objects.
            WebGLUtil.setUniforms(this.programInfo, uniformsThatAreTheSameForAllObjects);

            // Draw objects
            objects.forEach((object)=> {

                // Compute a position for this object based on the time.
                let worldMatrix = Matrix4.rotateX(object.xRotation * time);
                worldMatrix = Matrix4.multiply(worldMatrix,Matrix4.rotateY(object.yRotation * time));
                worldMatrix = Matrix4.multiply(worldMatrix,Matrix4.translation(0, 0, object.radius));
                uniformsThatAreComputedForEachObject.u_world = worldMatrix;
        
                // Multiply the matrices.
                uniformsThatAreComputedForEachObject.u_worldViewProjection = Matrix4.multiply(viewProjectionMatrix, worldMatrix);
                uniformsThatAreComputedForEachObject.u_worldInverseTranspose = Matrix4.transpose(Matrix4.invert(worldMatrix));
        
                // Set the uniforms we just computed
                WebGLUtil.setUniforms(this.programInfo, uniformsThatAreComputedForEachObject);
        
                // Set the uniforms that are specific to the this object.
                WebGLUtil.setUniforms(this.programInfo, object.materialUniforms);
        
                // Draw the geometry.
                gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
            });
  
        }
        this.draw = drawScene;
    }

    // public override draw(param?: any): void {
    //     const gl = Render.GL;

    // }
}